package com.rrd.itext.utils;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.bouncycastle.util.test.Test;

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontFactoryImp;
import com.itextpdf.text.FontProvider;
import com.itextpdf.text.Image;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Font.FontStyle;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.CMYKColor;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.PdfWriter;
import com.rrd.itext.obj.ResouceObject;
import com.rrd.itext.xmlBean.dataBean.DataXmlBaseBean;
import com.rrd.itext.xmlBean.dataBean.DataXmlPage;
import com.rrd.itext.xmlBean.dataBean.DataXmlSection;
import com.rrd.itext.xmlBean.fontBean.XmlFont;
import com.rrd.itext.xmlBean.fontBean.XmlFonts;
import com.rrd.itext.xmlBean.layoutBean.XmlBaseBean;
import com.rrd.itext.xmlBean.layoutBean.XmlCircle;
import com.rrd.itext.xmlBean.layoutBean.XmlImage;
import com.rrd.itext.xmlBean.layoutBean.XmlLine;
import com.rrd.itext.xmlBean.layoutBean.XmlPage;
import com.rrd.itext.xmlBean.layoutBean.XmlParagraph;
import com.rrd.itext.xmlBean.layoutBean.XmlPhrase;
import com.rrd.itext.xmlBean.layoutBean.XmlRectangle;
import com.rrd.itext.xmlBean.layoutBean.XmlSection;
import com.rrd.itext.xmlBean.layoutBean.XmlStyle;
import com.rrd.itext.xmlBean.layoutBean.XmlText;
import com.rrd.itext.xmlBean.layoutBean.XmlWaterStamper;
import com.rrd.itext.xmlBean.layoutBean.XmlZone;
import com.rrd.itext.xmlBean.layoutBean.iface.PdfElementRender;
import com.rrd.itext.xmlHandler.XmlHanlderCallBack;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;

public class PDFBuilder implements XmlHanlderCallBack {

	private Map<String, Object> resourceMap;
	private Map<String, XmlStyle> styleMap;
	private Map<String, String> fontMap;
	private PdfWriter writer;
	private Document document;
	private XmlPage currentPage;
	private int pageCount;
	private String resPath;
	private FontProvider fontProvider;

	private XmlBaseBean layout;

	public XmlBaseBean getLayout() {
		return this.layout;
	}

	public PDFBuilder(String resPath) {
		this.resPath = resPath;
	}

	public static void main(String[] args) throws IOException,
			DocumentException {

	}

	public void setFooter(PdfWriter writer) throws DocumentException,
			IOException {
		Font f = new Font(BaseFont.createFont());
		f.setColor(new BaseColor(120, 20, 127));
		f.setSize(13);
		f.setStyle(FontStyle.BOLD.getValue());

		HeaderFooter event = new HeaderFooter(f, this);
		writer.setBoxSize("page", new Rectangle(20, 15,
				layout.getPageWidth() - 20, 110));
		writer.setPageEvent(event);
	}

	@Override
    public void callBack(java.io.OutputStream output) throws Exception{
        
        // step 1
        try {
            Integer width = layout.getPageWidth();
            Integer height = layout.getPageHeight();
            Rectangle pageSize = new Rectangle(width, height);
            document = new Document(pageSize, 0, 0, 0, 0);
            // step 2
            writer = PdfWriter.getInstance(document, output);
            writer.setCompressionLevel(0);
            this.setFooter(writer);
            // step 3
            document.open();
            // step 4
            PdfContentByte cb = writer.getDirectContent();
            // get xml content
            List<XmlSection> sections = layout.getSections();
            Integer pageNumber = 0;
            for (XmlSection section : sections) {
                List<XmlPage> pages = section.getPages();
                for (XmlPage page : pages) {
                    currentPage = page;
                    if ("PDFs".equals(currentPage.getType())) {
                        String url = null;
                        if(this.getResourceMap().containsKey(currentPage.getName())){
                            Object obj = this.getResourceMap().get(currentPage.getName());
                            url = (String)obj;//this.getResourceMap().get(currentPage.getName());
                        }else{
                            continue;
                        }
                        byte[] remotePdf = HttpClientUtil.getInstence().requestURL(url);
                        if (remotePdf == null) {
                            continue;
                        }
                        PdfReader reader = new PdfReader(remotePdf);
                        int n = reader.getNumberOfPages();
                        for (int j = 1; j <= n; j++) {
                            PdfImportedPage importedPage = writer.getImportedPage(reader, j);
                            cb.addTemplate(importedPage, 1f, 0, 0, 1, 0, 0);
                            document.newPage();
                        }
                        continue;
                    } else if ("Condition".equals(currentPage.getType())) {
                        //Insert blank pages before the last two pages of Enrollment template to make the total total number of pages to be multiple of 4
                        //Just implement it as static
                        currentPage.setDisplayPageNumber(false);
                        String condition = currentPage.getDescription();
                        Integer currentPageNumber = this.getPageCount();
                        //multiple:4,rest:2
                        Integer baseNumber = Integer.valueOf(condition.substring("multiple:".length(),condition.indexOf(",", "multiple:".length())));
                        Integer restNumber = Integer.valueOf(condition.substring(condition.indexOf("rest:") + "rest:".length(), condition.length()));
                        Integer blankPageNumber = (baseNumber - (currentPageNumber + restNumber - 1) % baseNumber)  % baseNumber;
                        for (int i = blankPageNumber; i > 0; i--) {
                            renderPdfObject(currentPage.getZones().get(0).getRectangle(), cb, this);
                            newPage();
                        }
                        continue;
                    }
                    
                    this.renderWaterMark(cb, currentPage, this);
                    pageNumber++;
                    XmlZone previousRelativeZone = null;
                    List<XmlZone> zones = currentPage.getZones();
                    for (XmlZone zone : zones) {
                        boolean isRelative = zone.isRelative();
                        if(isRelative){
                            zone.calculatePosition(previousRelativeZone, height);
                        }
                        // image
                        renderPdfObject(zone.getImage(), cb, this);
                        // phrase
                        List<XmlPhrase> phrases = zone.getPhrases();
                        for (XmlPhrase phrase : phrases) {
                            renderPdfObject(phrase, cb, this);
                        }
                        // paragraph
                        renderPdfObject(zone.getParagraph(), cb, this);
                        // rectangle
                        renderPdfObject(zone.getRectangle(), cb, this);
                        // circle
                        renderPdfObject(zone.getCircle(), cb, this);
                        //Line
                        renderPdfObject(zone.getLine(), cb, this);
                        //Columns
                        renderPdfObject(zone.getColumns(), cb, this);
                        //table
                        renderPdfObject(zone.getXmlTable(), cb, this);
                        
                        //Save prevous zone
                        if(isRelative && zone.getImage() == null){
                            previousRelativeZone = zone;
                        }
                        if("ON".equalsIgnoreCase(layout.getDebugSwitch())){
                        	this.addBorders(zone,cb,this);                                
                        }
                    }
                    newPage();
                }
            }
            
            // Must use PdfContentByte to add text to an absolute position,
            // showTextAligned method has the x, y parameter
            // If the image is added first, then the text, the text is above the
            // image
            
            document.close();
        } catch (Exception ex) {
            ex.printStackTrace();
            throw ex;
        }
    }


    private FontProvider registerFamily(){
        FontFactoryImp factory = new FontFactoryImp();
        Map<String, String> fontMap = this.getFontMap();
        for(Entry<String, String> entry : fontMap.entrySet()){
            String key = entry.getKey();
            String value = entry.getValue();
            factory.register(value, key);
        }
        return factory;
    }
	
	private void addBorders(XmlZone zone, PdfContentByte cb,
			PDFBuilder pdfBuilder) throws Exception {
		XmlLine line = new XmlLine();
		line.setFontColorC(50);
		line.setFontColorM(100);
		line.setFontColorY(6);
		line.setFontColorK(5);
		line.setLineWidth(1);
		int offsetX = 0;
		int offsetY = 0;
		int width = 0;
		int height = 0;
		if (zone.getParagraph() != null) {
			XmlParagraph p = zone.getParagraph();
			offsetX = p.getOffsetX().intValue();
			offsetY = p.getOffsetY().intValue();
			width = p.getWidth();
			height = p.getHeight();

		}
		if (zone.getImage() != null) {
			XmlImage image = zone.getImage();
			offsetX = image.getOffsetX().intValue();
			offsetY = image.getOffsetY().intValue();
			width = image.getWidth();
			height = image.getHeight();
		}
		if (zone.getPhrases().size() != 0) {
			List<XmlPhrase> phrases = zone.getPhrases();
			for (XmlPhrase phrase : phrases) {
				offsetX = phrase.getOffsetX().intValue();
				offsetY = phrase.getOffsetY().intValue();
			}
		}
		if (zone.getParagraph() != null || zone.getImage() != null || zone.getPhrases() != null) {
			if (zone.getPhrases().size() != 0) {
				line.setFontColorC(0);
				line.setFontColorM(0);
				line.setFontColorY(0);
				line.setFontColorK(100);
				for (int i = 0; i < 2; i++) {
					line.setStartX(offsetX);
					line.setStartY(offsetY + ((i % 2) == 1 ? 20 : 0));
					line.setEndX(offsetX + ((i % 2) == 0 ? 30 : 0));
					line.setEndY(offsetY);
					renderPdfObject(line, cb, this);
				}
			} else {
				for (int i = 0; i < 4; i++) {
					line.setStartX(offsetX + ((i % 4) == 3 ? width : 0));
					line.setStartY(offsetY + ((i % 4) == 1 ? 0 : height));
					line.setEndX(offsetX + ((i % 4) == 2 ? 0 : width));
					line.setEndY(offsetY + ((i % 4) == 0 ? height : 0));
					renderPdfObject(line, cb, this);
				}
				this.addPharse(zone, offsetX, offsetY, height, width, cb, this);
			}
		}
	}
	private void addPharse(XmlZone zone,int offsetX, int offsetY, int height, int width,
			PdfContentByte cb, PDFBuilder pdfBuilder) throws Exception {
		XmlPhrase phrase=new XmlPhrase();
		phrase.setOffsetY(((Integer)(offsetY+height)).floatValue()-15);
		phrase.setOffsetX(((Integer)(offsetX+width)).floatValue()-50);
		XmlText text=new XmlText();
		text.setText("X:"+offsetX+" Y:"+offsetY);
		phrase.setText(text);
		List<XmlPhrase> phrases=new ArrayList<XmlPhrase>();
		phrases.add(phrase);
		for (XmlPhrase phrase1 : phrases) {
             renderPdfObject(phrase1, cb, this);
         }		
	}
	public void newPage() {
		document.newPage();
	}
	private static void renderPdfObject(PdfElementRender render,
			PdfContentByte cb, PDFBuilder builder) throws Exception {
		if (render != null) {
			render.render(cb, builder);
		}
	}

	private void renderWaterMark(PdfContentByte cb, XmlPage page,
			PDFBuilder builder) throws Exception {// Test Water Mark
		if (page.getZones() == null) {
			return;
		}
		XmlWaterStamper waterStamper;
		for (XmlZone zone : page.getZones()) {
			if (zone.getWaterStamper() != null) {
				waterStamper = zone.getWaterStamper();
				if (waterStamper == null) {
					continue;
				}
				waterStamper.render(cb, builder);
			}
		}
	}

	public Map<String, XmlStyle> getStyleMap() {
		return styleMap;
	}

	public String getResPath() {
		return resPath;
	}

	public void setResPath(String resPath) {
		this.resPath = resPath;
	}

	public Map<String, String> getFontMap() {
		return fontMap;
	}

	@Override
	public void processLayout(XmlBaseBean layout) throws Exception {
		this.layout = layout;
		Map<String, XmlStyle> styleMap = new HashMap<String, XmlStyle>();
		if (layout == null || layout.getStyles() == null
				|| layout.getStyles().size() == 0) {
			this.styleMap = styleMap;
			return;
		}
		for (XmlStyle s : layout.getStyles()) {
			styleMap.put(s.getName(), s);
		}
		this.styleMap = styleMap;
	}

	@Override
	public void processData(DataXmlBaseBean data) throws Exception {
		Map<String, Object> resouceMap = ResouceObject.makeResouceMap(data);
		this.setResourceMap(resouceMap);
	}

	@Override
	public void processFonts(XmlFonts fonts) throws Exception {
		this.fontMap = ResouceObject.makeFontMap(fonts);
		this.fontProvider = this.registerFamily();
	}

	public PdfWriter getWriter() {
		return writer;
	}

	public void setWriter(PdfWriter writer) {
		this.writer = writer;
	}

	public XmlPage getCurrentPage() {
		return currentPage;
	}

	public void setCurrentPage(XmlPage currentPage) {
		this.currentPage = currentPage;
	}

	public int getPageCount() {
		return pageCount;
	}

	public void setPageCount(int pageCount) {
		this.pageCount = pageCount;
	}

	public Map<String, Object> getResourceMap() {
		return resourceMap;
	}

	public void setResourceMap(Map<String, Object> resourceMap) {
		this.resourceMap = resourceMap;
	}

	public FontProvider getFontProvider() {
        return fontProvider;
    }

    public void setFontProvider(FontProvider fontProvider) {
        this.fontProvider = fontProvider;
    }

    class PdfWaterMark {
		private int pageNumber;
		private XmlWaterStamper waterStamper;

		public int getPageNumber() {
			return pageNumber;
		}

		public void setPageNumber(int pageNumber) {
			this.pageNumber = pageNumber;
		}

		public XmlWaterStamper getWaterStamper() {
			return waterStamper;
		}

		public void setWaterStamper(XmlWaterStamper waterStamper) {
			this.waterStamper = waterStamper;
		}

	}
}